#include "form_chart_info.h"
#include "ui_form_chart_info.h"

// add necessary includes here
#include "ui/share/form_plot_dlg.h"
#include "utils/can/can_bll_t1c.h"

static int gs_nCycleIdx = 0;
static QQueue<QMap<int, QStringList>>  gs_queFluoValues;
static QSemaphore gs_semaphore(0);

/*
 * Function:    run
 * Description: Default thread function
 * Paramer:     void
 * Return:      void
 */
void ChartInfoThread::run()
{
    qDebug() << Q_FUNC_INFO;

    while(!m_bStopped) {
        if(!gs_queFluoValues.isEmpty()) {
            qDebug() << Q_FUNC_INFO << gs_queFluoValues.size();

            // Pop first element in queue
            QMap<int, QStringList> mapFluoValues = gs_queFluoValues.takeFirst();

            // Refresh the fluoresence chart view
            emit signalRefreshFluoChart(mapFluoValues);

            gs_semaphore.acquire();
        }

        // Wait for a moment (Time interval for intra group PCR module queries)
        QThread::msleep(10);

        // Processes all pending events
        QCoreApplication::processEvents();
    }
}


FormChartInfo::FormChartInfo(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::FormChartInfo)
{
    ui->setupUi(this);

    // After constructing object
    this->_initialze();

    // Initialize control's UI
    this->_initUi();

    this->show();
}

FormChartInfo::~FormChartInfo()
{
    qDebug() << Q_FUNC_INFO;
    delete ui;

//    if(m_thrTempWorker.isRunning()) {
//        m_thrTempWorker.quit();
//        m_thrTempWorker.wait();
//    }

//    if(m_thrFluoWorker.isRunning()) {
//        m_thrFluoWorker.quit();
//        m_thrFluoWorker.wait();
//    }

//    if(m_thrMeltWorker.isRunning()) {
//        m_thrMeltWorker.quit();
//        m_thrMeltWorker.wait();
//    }

//    if(m_pChartCurvesThread->isRunning()) {
//        m_pChartCurvesThread->stop();
//        m_pChartCurvesThread->terminate();
//    }

    if(m_pTempChartDlg) {
        delete m_pTempChartDlg;
        m_pTempChartDlg = nullptr;
    }

    if(m_pFluoChartDlg) {
        delete m_pFluoChartDlg;
        m_pFluoChartDlg = nullptr;
    }

    if(m_pMeltChartDlg) {
        delete m_pMeltChartDlg;
        m_pMeltChartDlg = nullptr;
    }
}

/*
 * Function:    _initialze
 * Description: After constructing object, initialize the object
 * Paramer:     void
 * Return:      void
 */
void FormChartInfo::_initialze()
{
    // Registers the type name typeName for the type QMap<int, QStringList>
    qRegisterMetaType<QMap<int, QStringList>>("QMap<int, QStringList>");

    if(!m_pChartCurvesThread) {
        m_pChartCurvesThread = new ChartInfoThread(this);
        Q_ASSERT(m_pChartCurvesThread);
    }

    // Signal-slot initialization
    std::vector<bool> vecConnect;
    vecConnect.push_back(QObject::connect(this, SIGNAL(signalRefreshUI()), this, SLOT(slotRefreshUI()), static_cast<Qt::ConnectionType>(Qt::AutoConnection | Qt::UniqueConnection)));
    vecConnect.push_back(QObject::connect(m_pChartCurvesThread, SIGNAL(signalRefreshFluoChart(const QMap<int, QStringList> &)), this, SLOT(slotRefreshFluoChart(const QMap<int, QStringList> &)), static_cast<Qt::ConnectionType>(Qt::AutoConnection | Qt::UniqueConnection)));
    for(int i=0; i<(int)vecConnect.size(); i++) {
        Q_ASSERT(vecConnect.at(i));
    }

    m_pChartCurvesThread->start();
}

/*
* Function:		_initUi
* Description:	Init the window style
* Paramer:		void
* Return:		void
*/
void FormChartInfo::_initUi()
{
    // Displays the background image of the window
    QWidget::setAttribute(Qt::WA_StyledBackground);

    SET_STYLE_SHEET(QString(":/%1/home/homepage/%2.qss").arg("skin_1").arg(this->objectName()));

    // Load a temperature chart
    if(!m_pTempChartDlg) {
        m_pTempChartDlg = new FormPlotDlg(this);
        Q_ASSERT(m_pTempChartDlg);

        m_pTempChartDlg->selectChartType(E_CT_TEMP);
//        m_pTempChartDlg->moveToThread(&m_thrTempWorker);
//        bool bConn = QObject::connect(&m_thrTempWorker, &QThread::finished, m_pTempChartDlg, &QObject::deleteLater);
//        Q_ASSERT(bConn);
    }

    if(m_pTempChartDlg) ui->tabWidget_Charts->addTab(m_pTempChartDlg, tr("Temperature"));

    // Load a fluoresence chart
    if(!m_pFluoChartDlg) {
        m_pFluoChartDlg = new FormPlotDlg(this);
        Q_ASSERT(m_pFluoChartDlg);

        m_pFluoChartDlg->selectChartType(E_CT_FLUO);
//        m_pFluoChartDlg->moveToThread(&m_thrFluoWorker);
//        bool bConn = QObject::connect(&m_thrFluoWorker, &QThread::finished, m_pFluoChartDlg, &QObject::deleteLater);
//        Q_ASSERT(bConn);
    }

    if(m_pFluoChartDlg) ui->tabWidget_Charts->addTab(m_pFluoChartDlg, tr("Fluoresence"));

    // Load a melting chart
    if(!m_pMeltChartDlg) {
        m_pMeltChartDlg = new FormPlotDlg(this);
        Q_ASSERT(m_pMeltChartDlg);

        m_pMeltChartDlg->selectChartType(E_CT_MELT);
//        m_pMeltChartDlg->moveToThread(&m_thrMeltWorker);
//        bool bConn = QObject::connect(&m_thrMeltWorker, &QThread::finished, m_pMeltChartDlg, &QObject::deleteLater);
//        Q_ASSERT(bConn);
    }

    if(m_pMeltChartDlg) ui->tabWidget_Charts->addTab(m_pMeltChartDlg, tr("Melting"));
}

/*
* Function:		changeEvent
* Description:	Triggers an event before it is switch language
* Paramer:		QShowEvent *e - the pointer of "QEvent"
* Return:		void
*/
void FormChartInfo::changeEvent(QEvent *e)
{
    switch (e->type()) {
    case QEvent::LanguageChange:
    {
        qDebug() << Q_FUNC_INFO;
        ui->retranslateUi(this);

        break;
    }
    default:
        break;
    }

    QWidget::changeEvent(e);
}

/*
 * Function:    displayWindow
 * Description: Display window
 * Paramer:     void
 * Return:      void
 */
void FormChartInfo::displayWindow()
{
    this->show();
}

/*
 * Function:    getChartData
 * Description: Get the chart data
 * Paramer:     QMap<unsigned int, QVector<QPointF>> &mapValue - chart data
 * Return:      void
 */
int FormChartInfo::getChartData(int nChartType, QMap<unsigned int, QVector<QPointF>> &mapValue)
{
    mapValue.clear();
    qDebug() << Q_FUNC_INFO << nChartType;

    int nRetVal = 0;

    switch (nChartType) {
    case E_CT_TEMP:
        m_pTempChartDlg->getChartData(nChartType, mapValue);
        break;
    case E_CT_FLUO:
        m_pFluoChartDlg->getChartData(nChartType, mapValue);
        break;
    case E_CT_MELT:
        m_pMeltChartDlg->getChartData(nChartType, mapValue);
        break;
    }

    nRetVal = mapValue.size();

    qDebug() << Q_FUNC_INFO << nRetVal;
    return nRetVal;
}

void FormChartInfo::on_pushButton_Cancel_clicked()
{
    this->hide();
}

void FormChartInfo::slotRefreshUI()
{
    qDebug() << Q_FUNC_INFO;

    gs_nCycleIdx = 0;
    gs_queFluoValues.clear();

    // Clear chart data
    if(m_pTempChartDlg) m_pTempChartDlg->clearChartData(E_CT_TEMP);
    if(m_pFluoChartDlg) m_pFluoChartDlg->clearChartData(E_CT_FLUO);
    if(m_pMeltChartDlg) m_pMeltChartDlg->clearChartData(E_CT_MELT);
}

void FormChartInfo::slotHeatValueReceived(unsigned int unCanId, QList<float> lstDataValue)
{
    qDebug() << Q_FUNC_INFO << unCanId << lstDataValue;

    if(CanVars::gs_nHeatIapCanID_CH1 == unCanId - 1) {
        // Heating for extract
        if(DEF_HEAT_CHANNEL_SIZE == lstDataValue.size()) {
            for(int i=0; i<DEF_HEAT_CHANNEL_SIZE; i++) {
                float fTempVal = lstDataValue.at(i);
                if(0 < fTempVal) {
                    // // Attach data into the chart
                    m_pTempChartDlg->addChartData(E_CT_TEMP, i, fTempVal);
                }
            }
        }

        // Update the range of chart
        m_pTempChartDlg->updateChartRange(E_CT_TEMP);
    }
}

/*
* Function:		slotHeatingValueReceived
* Description:	Receive a fluoresence value on sample hole
* Paramer:      QList<float> lstDataValue - fluoresence values
* Return:       void
*/
void FormChartInfo::slotFluoValueReceived(unsigned int unCanId, const QMap<int, QStringList> & mapFluoValues)
{
    qDebug() << Q_FUNC_INFO << unCanId << mapFluoValues.size();

    if((int)(CanVars::gs_nPcrCanID_CH1 + 1) == (int)unCanId) {
        // Push data into queue & processing later
        gs_queFluoValues << mapFluoValues;

        qDebug() << Q_FUNC_INFO << unCanId << gs_queFluoValues.size();
    }
}

/*
* Function:		slotRefreshFluoChart
* Description:	Receive a fluoresence value on sample hole
* Paramer:      QList<float> lstDataValue - fluoresence values
* Return:       void
*/
void FormChartInfo::slotRefreshFluoChart(const QMap<int, QStringList> & mapFluoValues)
{
    qDebug() << Q_FUNC_INFO << mapFluoValues.size();

    if(DEF_T1C_HOLE_COUNT == mapFluoValues.size()) {
        if(m_pFluoChartDlg) {
            QMapIterator<int, QStringList> itor(mapFluoValues);
            while (itor.hasNext()) {
                itor.next();

                int nHoleIdx = itor.key();
                QStringList lstFluoVal = itor.value();

//                qDebug() << Q_FUNC_INFO << nHoleIdx << lstFluoVal;

                // Insert curve data
                if(0 <= nHoleIdx && DEF_T1C_HOLE_COUNT > nHoleIdx && DEF_T1C_HOLE_COUNT == lstFluoVal.size()) {
                    // light wave: ATTO425/FAM/VIC/CY3/ROX/CY5/CY5.5/CY7
                    for(int i=0; i<DEF_T1C_HOLE_COUNT; i++) {
                        int nLineIdx = nHoleIdx * DEF_T1C_HOLE_COUNT + i;
                        float fFluoVal = lstFluoVal.at(i).toFloat();

                        // Attach data into the chart
                        m_pFluoChartDlg->addChartData(E_CT_FLUO, nLineIdx, gs_nCycleIdx, fFluoVal);
                    }
                }
            }

            qDebug() << Q_FUNC_INFO << "======R" << gs_nCycleIdx;

            gs_nCycleIdx++;
            m_pFluoChartDlg->updateChartRange(E_CT_FLUO);
        }
    }

    gs_semaphore.release();
}
